package org.dmikis.task11.engine;

import static java.lang.Math.*;

import org.dmikis.task11.PhasePoint;
import org.dmikis.task11.parameter.*;


public class EulerEngine extends BasicEngine {
    
    protected EulerEngine() {
	super();
	parameterA = new EngineParameter<Double>("A", 0.5);
	this.parameters.add(this.parameterA);
    }
    
    public static Engine getInstance() {
	return EulerEngine.engine;
    }
    
    public void run() {
	super._isRunning = true;
	double current_time = 0.0;
	double tmpV1, tmpV2, tmpTetta1, tmpTetta2;
	while (current_time < 50.0) {
	    this._timeStep = 2.0*BasicEngine.TIME_STEP;
	    PhasePoint current_point = points.get(points.size() - 1);
	    do {
		this._timeStep = 0.5 * this._timeStep;
		tmpV1 = current_point.getV() + evaluateDv(current_point.getV(), current_point.getTetta()) * this._timeStep;
        	tmpTetta1 = current_point.getTetta() + evaluateDtetta(current_point.getV(), current_point.getTetta()) * this._timeStep;
        	tmpV2 = current_point.getV() + evaluateDv(current_point.getV(), current_point.getTetta()) * this._timeStep * 0.5;
        	tmpTetta2 = current_point.getTetta() + evaluateDtetta(current_point.getV(), current_point.getTetta()) * this._timeStep * 0.5;
        	tmpV2 += evaluateDv(tmpV2, tmpTetta2) * this._timeStep * 0.5;
        	tmpTetta2 += evaluateDtetta(tmpV2, tmpTetta2) * this._timeStep * 0.5;
	    } while ((abs(tmpV1 - tmpV2) > _error) || (abs(tmpTetta1 - tmpTetta2) > _error));
	    points.add(new PhasePoint(tmpV1, tmpTetta1, current_time += this._timeStep));
	}
	super._isRunning = false;
    }
    
    protected double evaluateDv(double currV, double currTetta) {
	return -sin(currTetta) - parameterA.getValue();
    }
    
    protected double evaluateDtetta(double currV, double currTetta) {
	return currV - cos(currTetta) / currV;
    }
    
    @Override
    public void setErrorValue(double e) {
	_error = e;
    }
    
    @Override
    public double getTimeStep() {
	return this._timeStep;
    }
    
    protected double _timeStep;
    protected EngineParameter<Double> parameterA;
    
    protected static Engine engine = new EulerEngine();
    
    protected double _error = Double.MAX_VALUE;

}
